#include "include/lrms.h"
#include "include/protocol.h"		/* for protocol constants */
#include "include/queue.c" 		/* for clients/servers queues */
#include "include/llist.c"		/* for servers queue/list */
#include "include/rsccollector.c"	/* for resource affectation list */
#include <pthread.h>			/* for threads uses */

/* Server status type */
typedef enum ServStatus {
	IDLE = 0, BUSY = 1
} ServStatus;

/* server structure */
typedef struct serveraddr {
	struct sockaddr_in addr;//SA_in addr;
	char reg_message[MAXMES];//plus
	ServStatus status;
	
} severaddr;

/* client structure */
typedef struct clientaddr {
	struct sockaddr_in addr;//SA_in addr;
	char dem_message[MAXMES];
	//int status;
	
} clientaddr;

/* communication structure */
typedef struct communication_t {
	struct clientaddr client;
	struct serveraddr server;
	int read_ok; 
	pthread_cond_t read_cond;
	pthread_mutex_t mutex;

} communication_t;

/* Communication elt */
struct communication_t communication;

/* clients queue */
Queue clnt_queue;

/* servers queue */
LList serv_queue;
Queue serv_idle_queue;
// we can insert idle queue pointer in the llist

/* resource affectation list */
RList rsc_aff;

/* scheduler thread function */
void * sched_thread_fn(void * unused);

/* client thread function */
void * clnt_thread_fn(void * communication);

/* client resource getback function */
void * clnt_rs_getback(void * tid);

/* =============================================================== *
 *			Prototypes				   *
 * =============================================================== */

int create_bind_udp_socket( int * sockSched, const char * ipAddr, int NoPort, struct sockaddr_in * addrSched);
int receive_message( int sockSched, struct sockaddr_in * addrPeer, char * message);
int save_peer( int sockSched, struct sockaddr_in addrPeer, const char * message, LList * serv_queue, Queue * serv_idle_queue, Queue * clnt_queue);
int get_client( Queue * clnt_queue, struct clientaddr * client); 
int get_server( Queue * serv_idle_queue, struct serveraddr * server);
int create_client_thread(struct clientaddr client, struct serveraddr server, pthread_t * client_tid);
int save_affectation( struct serveraddr * server, struct clientaddr * client, pthread_t client_tid);
int create_udp_socket(int * sockfd);
int start_communication( int sockNew, struct clientaddr client, struct sockaddr_in server);


/* =============================================================== *
 *			Globals Variables			   *
 * =============================================================== */

static int log_file = 0;	// log file
static char log_msg[MAXLOGMSG];


/* =============================================================== *
 *			MAIN FUNCTION				   *
 * =============================================================== */

int main(int argc, char* argv[])
{
	int sockSched;			// socket to be binded to the WKP
	struct sockaddr_in addrSched;	// scheduler address
	struct sockaddr_in addrPeer;	// Peer Socket (client or server) address
	char message[MAXMES];
	int returnStatus = 0;		// for each fuction call
	
	pthread_t sched_thread;

	/* --------------------------- get arguments -------------------------- */
	
	/* check for the right number of arguments */
	if (argc != 2)
	{
		sprintf(log_msg, "Usage: %s <port>.\n", argv[0]);
		print_msg( log_msg, 1, 1, 0, log_file);
		exit(EXIT_FAILURE);
	}
	
	
	/* ------------------------ open log file ---------------------- */
	
	log_file = open( "scheduler.log", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR );
	
	
	/* --------------------------- initialisation -------------------------- */
	
	/* initialisation servers queue */
	llist_init( &serv_queue);
	queue_init( &serv_idle_queue);
	
	/* initialisation clients queue */
	queue_init(&clnt_queue);
	
	/* initialisation resource affectation list */
	rlist_init( &rsc_aff);
	
	/* ------------------- create the scheduling thread -------------------- */
	
	if ( (returnStatus = pthread_create(& sched_thread, NULL, sched_thread_fn, NULL)) != 0 )
	{
		//sprintf(log_msg, "%s\n",strerror(ret)); est ce quil ecrit ds errno
		//print_msg( log_msg, 0, 1, 1, log_file);
		perror("main thread : pthread_create");//to change
		exit(EXIT_FAILURE);
	}
	
	// debug
	sprintf(log_msg, "MainThread : scheduler thread %ld created .\n", (long) sched_thread);
	print_msg( log_msg, 1, 0, 1, log_file);

	/* --------------------- create Scheduler WKS socket ------------------ */
	
	returnStatus = create_bind_udp_socket( &sockSched, NULL, atoi(argv[1]), &addrSched);
	if (returnStatus != EXIT_SUCCESS )
	{
		sprintf(log_msg, "MainThread::Create_UDP_Socket : Error : Socket creation.\n");
		print_msg( log_msg, 0, 1, 1, log_file);
		exit(EXIT_FAILURE);
	}
	sprintf(log_msg, "MainThread::Create_UDP_Socket : Socket %d created and binded at %s@%d successfully.\n", sockSched, inet_ntoa(addrSched.sin_addr), ntohs(addrSched.sin_port));
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* ignorer le signal du fils, on utilisera sigaction pour le reste des signaux 
	signal(SIGCHLD, SIG_IGN);*/
	
	/* loop forever */
	while (1)
	{
		
		/* --------------------- Receive Messges from C/S -------------------- */
		
		returnStatus = receive_message( sockSched, &addrPeer, message);
		if ( returnStatus != EXIT_SUCCESS )
		{
			sprintf( log_msg, "MainThread::Receive_Message : Error : Couldn't receive message .\n");
			print_msg( log_msg, 0, 1, 1, log_file);
			continue;
		}
		sprintf(log_msg, "MainThread:: Receive_Message : Message Received %s from %s@%d .\n", message, inet_ntoa(addrPeer.sin_addr), ntohs(addrPeer.sin_port));
		print_msg( log_msg, 1, 0, 1, log_file);
		
		/* --------------------- Save demand/registration  ------------------- */
		
		returnStatus = save_peer( sockSched, addrPeer, message, &serv_queue, &serv_idle_queue, &clnt_queue); 
		if( returnStatus != EXIT_SUCCESS )
		{
			sprintf(log_msg, "MainThread:: Save_Peer : Error : Couldn't register %s@%d.\n", inet_ntoa(addrPeer.sin_addr), ntohs(addrPeer.sin_port));
			print_msg( log_msg, 0, 1, 1, log_file);
		}
		sprintf(log_msg, "MainThread:: Save_Peer : Peer  %s@%d registred successfully.\n", inet_ntoa(addrPeer.sin_addr), ntohs(addrPeer.sin_port));
		print_msg( log_msg, 1, 0, 1, log_file);
	}
		
		
		
	/* ---------------------------- cleanup --------------------------- */
	close(log_file);
	close(sockSched);
	return EXIT_SUCCESS;
}

/* =============================================================== *
 *		     SCHEDULER THREAD FUNCTION			   *
 * =============================================================== */

void * sched_thread_fn(void * unused)
{
	
	struct clientaddr * client; 
	struct serveraddr * server;
	int returnStatus;
	pthread_t clnt_thread;
	
	/* --------------- init communication mutex & condition ---------------- */
	 
	pthread_mutex_init(& (communication.mutex), NULL);
	pthread_cond_init(& (communication.read_cond), NULL);
	
	/* ------------------------- loop forever ------------------------------ */
	
	while(1)
	{
		/* --------------------- reset communication --------------------------- */
		
		memset(&communication.client, 0, sizeof(struct clientaddr));
		memset(&communication.server, 0, sizeof(struct serveraddr));
		communication.read_ok = 0;
		
		/* -------- get client address or go sleep if the queue is empty ------- */
		
		pthread_mutex_lock(& (clnt_queue.mutex));
		sprintf( log_msg, "SchdThread : get queue mutex for /verifing if it is empty. /dequeuing client.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		returnStatus = queue_dequeue(&clnt_queue, (void**) &client);
		pthread_mutex_unlock(& (clnt_queue.mutex));
		sprintf( log_msg, "SchdThread : less queue mutex for /verifing if it is empty. /dequeuing client.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		if( returnStatus != EXIT_SUCCESS)
		{
			sprintf( log_msg, "SchdThread : client queue is empty  .. waiting for signal.\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			
			pthread_mutex_lock( &(clnt_queue.mutex_non_empty) );
			pthread_cond_wait( &(clnt_queue.cond_non_empty) , &(clnt_queue.mutex_non_empty) );
			
			sprintf( log_msg, "SchdThread : client receiving signal (non-empty) .. continue.\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			pthread_mutex_unlock( &(clnt_queue.mutex_non_empty) );

			// dequeue the added client
			pthread_mutex_lock(& (clnt_queue.mutex));
			sprintf( log_msg, "SchdThread : get queue mutex for dequeuing the added client .\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			
			queue_dequeue(&clnt_queue, (void**) &client);
			pthread_mutex_unlock(& (clnt_queue.mutex));
			sprintf( log_msg, "SchdThread : less queue mutex for dequeuing the added client .\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			
		}
		
		
		// debug
		sprintf(log_msg, "SchdThread : get client %s@%d at %p.\n", inet_ntoa(client->addr.sin_addr), ntohs(client->addr.sin_port), client);  
		print_msg( log_msg, 1, 0, 1, log_file);
		
		/* fill the client address in the communication struct */
		memcpy( &(communication.client.addr), &(client->addr), sizeof(struct sockaddr_in));	
		strcpy(communication.client.dem_message, client->dem_message);
			
		
		/* -------- get server address or go sleep if the queue is empty ------- */
		
		pthread_mutex_lock(& (serv_idle_queue.mutex));
		sprintf( log_msg, "SchdThread : get queue mutex for /verifing if it is empty. /dequeuing server.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		returnStatus = queue_dequeue(&serv_idle_queue, (void**) &server);
		pthread_mutex_unlock(& (serv_idle_queue.mutex));
		sprintf( log_msg, "SchdThread : less queue mutex for /verifing if it is empty. /dequeuing server.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		if( returnStatus != EXIT_SUCCESS)
		{
			sprintf( log_msg, "SchdThread : server idle queue is empty  .. waiting for signal.\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			
			pthread_mutex_lock( &(serv_idle_queue.mutex_non_empty) );
			pthread_cond_wait( &(serv_idle_queue.cond_non_empty) , &(serv_idle_queue.mutex_non_empty) );
			
			sprintf( log_msg, "SchdThread : server idle queue receiving signal (non-empty) .. continue.\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			
			pthread_mutex_unlock( &(serv_idle_queue.mutex_non_empty) );

			/* dequeue the added server */
			pthread_mutex_lock(& (serv_idle_queue.mutex));
			sprintf( log_msg, "SchdThread : get idle queue mutex for dequeuing the added server .\n");
			print_msg( log_msg, 1, 0, 1, log_file);
			
			queue_dequeue(&serv_idle_queue, (void**) &server);
			pthread_mutex_unlock(& (serv_idle_queue.mutex));
			sprintf( log_msg, "SchdThread : less idle queue mutex for dequeuing the added server .\n"); 
			print_msg( log_msg, 1, 0, 1, log_file);
			/* set server status to BUSY */
			server->status = BUSY;
			
		}
		
		// debug
		sprintf(log_msg, "SchdThread : get server %s@%d at %p.\n", inet_ntoa(server->addr.sin_addr), ntohs(server->addr.sin_port), server); 
		print_msg( log_msg, 1, 0, 1, log_file);
		
		/* fill the server address in the communication struct */	
		memcpy( &(communication.server.addr), &(server->addr), sizeof(struct sockaddr_in));	
		strcpy(communication.server.reg_message, server->reg_message);
		
		/* -------- create a thread that will maintain that communication ------ */
		
		returnStatus = pthread_create(& clnt_thread, NULL, clnt_thread_fn, (void*) &communication);
		if ( returnStatus != 0 ){
			//sprintf(log_msg, "%s\n",strerror(ret)); est ce quil ecrit ds errno
			//print_msg( log_msg, 0, 1, 1, log_file);
			perror("SchdThread : pthread_create");//to change
			//exit(EXIT_FAILURE);
		}
		
		/* detach client thread */
		pthread_detach(clnt_thread);
		
		/* --------- save the client and the resource affected to him ---------- */
		
		pthread_mutex_lock( &(rsc_aff.mutex) );
		sprintf( log_msg, "SchdThread : get resource affectation queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		rlist_insert( &rsc_aff, clnt_thread, client, server);
		pthread_mutex_unlock( &(rsc_aff.mutex) );
		sprintf( log_msg, "SchdThread : less resource affectation queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		
		// debug
		sprintf(log_msg, "SchdThread : thread %ld created for C:%s@%d S:%s@%d.\n", (long) clnt_thread, inet_ntoa(communication.client.addr.sin_addr), ntohs(communication.client.addr.sin_port), inet_ntoa(communication.server.addr.sin_addr), ntohs(communication.server.addr.sin_port)); 
		print_msg( log_msg, 1, 0, 1, log_file);
		
		/* ----- wait until the client read the communication structure ------- */
		pthread_mutex_lock( &(communication.mutex) );
		if(communication.read_ok != 1) {
			pthread_cond_wait( &(communication.read_cond), &(communication.mutex) );
		}
		pthread_mutex_unlock( &(communication.mutex));
				
		sprintf(log_msg, "SchdThread : thread %ld with C:%s@%d S:%s@%d : communication readed.\n", (long) clnt_thread, inet_ntoa(communication.client.addr.sin_addr), ntohs(communication.client.addr.sin_port), inet_ntoa(communication.server.addr.sin_addr), ntohs(communication.server.addr.sin_port)); 			print_msg( log_msg, 1, 0, 1, log_file);
		
	}
	
	pthread_exit(NULL);
}

/* =============================================================== *
 *		     CLIENT THREAD FUNCTION			   *
 * =============================================================== */

void * clnt_thread_fn(void * com)
{
	int sockNew;
	struct sockaddr_in addrNew;
	struct clientaddr client;
	struct sockaddr_in addrServ;
	socklen_t addrlen;
	int returnStatus;
	struct communication_t clnt_communication;// = *((struct communication_t *) com);
	
	/* --------------------------- initialisation -------------------------- */
	
	/* get the communication structure */
	memcpy(&clnt_communication, &communication, sizeof(struct communication_t));
	
	/* send signal : read communication : OK */
	pthread_mutex_lock( &(communication.mutex));
	communication.read_ok = 1;
	pthread_cond_signal( &(communication.read_cond));
	pthread_mutex_unlock( &(communication.mutex)); 
	
	// debug
	sprintf(log_msg, "ClntThread::Init  %ld : Send signal to the scheduler thread : communication readed.\n",(long) pthread_self());
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* save client resource getback function */
	pthread_cleanup_push((void *)clnt_rs_getback,(void *) pthread_self());
	
	/* get server address */
	memset(&addrServ, 0, sizeof(struct sockaddr_in));
	memcpy(&addrServ, &(clnt_communication.server.addr), sizeof(addrServ));
	
	/* get client struct */
	memset(&client, 0, sizeof(struct clientaddr));
	memcpy(&client, &(clnt_communication.client), sizeof(client));

	// debug
	sprintf(log_msg, "ClntThread::Init %ld : Starts with arg C:%s@%d S:%s@%d.\n",(long) pthread_self(), inet_ntoa(client.addr.sin_addr), ntohs(client.addr.sin_port), inet_ntoa(addrServ.sin_addr), ntohs(addrServ.sin_port)); 
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* -------------------------- create new socket ------------------------ */

	returnStatus = create_udp_socket( &sockNew);
	if ( returnStatus != EXIT_SUCCESS)
	{
		sprintf(log_msg, "ClntThread::Create_Udp_Socket %ld : Error : cannt create socket for communication.\n",(long) pthread_self());
		print_msg( log_msg, 0, 1, 1, log_file);
		pthread_exit(NULL);
	}
	
	/* ------------------------- start communication ----------------------- */
	
	returnStatus = start_communication( sockNew, client, addrServ);
	if ( returnStatus != EXIT_SUCCESS)
	{
		sprintf(log_msg, "ClntThread::Start_Communication %ld : Error : Couldn't start communication.\n",(long) pthread_self());
		print_msg( log_msg, 0, 1, 1, log_file);
		pthread_exit(NULL);
	} 
	sprintf(log_msg, "ClntThread::Start_Communication %ld : Communication Done.\n",(long) pthread_self());
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* ----------------------------- free server --------------------------- */
	
	pthread_cleanup_pop(1);
	
	
	// debug
	sprintf(log_msg, "ClntThread %ld : Exited .\n", (long) pthread_self());
	print_msg( log_msg, 1, 0, 1, log_file);
	pthread_exit(NULL);
}


/* =============================================================== *
 *		          CLNT RS GETBACK			   *
 * =============================================================== */

void * clnt_rs_getback(void * tid)
{
	int i = 0;
	pthread_t my_tid = (pthread_t) tid;
	struct serveraddr * resource;
	struct clientaddr * client_addr;
	
	pthread_mutex_lock( &(rsc_aff.mutex) );
	sprintf( log_msg, "main thread : get resource affectation queue mutex.\n");
	print_msg( log_msg, 1, 0, 1, log_file);
	
	rlist_find(&rsc_aff, my_tid, &client_addr, &resource);
	pthread_mutex_unlock( &(rsc_aff.mutex) );
	sprintf( log_msg, "main thread : less resource affectation queue mutex.\n");
	print_msg( log_msg, 1, 0, 1, log_file);
	
	
	if( resource != NULL){
	
		// get resource back
		resource->status = IDLE;
				
		pthread_mutex_lock( &(serv_idle_queue.mutex) );
		sprintf( log_msg, "collect fct %ld : get servers idle queue mutex.\n", (long) my_tid);
		print_msg( log_msg, 1, 0, 1, log_file);
		
		queue_enqueue(&serv_idle_queue,(void*) resource);
		pthread_mutex_unlock( &(serv_idle_queue.mutex) );
		sprintf( log_msg, "collect fct %ld : less servers queue mutex.\n", (long) my_tid);
		print_msg( log_msg, 1, 0, 1, log_file);
				
		// send a signal if the filed elt is the first
		pthread_mutex_lock( &(serv_idle_queue.mutex_non_empty) );
		pthread_cond_signal( &(serv_idle_queue.cond_non_empty) );
		sprintf( log_msg, "collect fct %ld : send signal non-empty servers.\n", (long) my_tid);
		print_msg( log_msg, 1, 0, 1, log_file);
		pthread_mutex_unlock( &(serv_idle_queue.mutex_non_empty) );
		
		sprintf(log_msg, "collect fct %ld : get back one resource : Server %s@%d.\n", (long) my_tid, inet_ntoa( (resource->addr).sin_addr), ntohs( (resource->addr).sin_port) );
		print_msg( log_msg, 1, 0, 1, log_file);
		sprintf(log_msg, "collect fct %ld : Client %s@%d : leaves the system .\n", (long) my_tid, inet_ntoa( (client_addr->addr).sin_addr), ntohs( (client_addr->addr).sin_port) );
		print_msg( log_msg, 1, 0, 1, log_file);
		
		free(client_addr);
	
	} else
		sprintf( log_msg, "collect fct %ld : cann't find tid , cannt free affected resource.\n", (long) my_tid);
		print_msg( log_msg, 1, 0, 1, log_file);
	
}

/* =============================================================== *
 *		     CREATE BIND UDP SOCKET			   *
 * =============================================================== */

int create_bind_udp_socket(int * sockSched, const char * ipAddr, int NoPort, struct sockaddr_in * addrSched)
{
	
	int returnStatus = 0;
	
	/* create udp socket */
	*sockSched = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (*sockSched < 0)
	{
		perror("MainThread : socket");
		return EXIT_FAILURE;
	}
	
	/* set up the scheduler address and port */
	/* use INADDR_ANY to bind to all local addresses */
	memset(addrSched, 0, sizeof(struct sockaddr_in));
	addrSched->sin_family = AF_INET;
	if ( ipAddr == NULL )
		addrSched->sin_addr.s_addr = htonl(INADDR_ANY);
	addrSched->sin_port = htons(NoPort);
	
	/* bind to the socket */
	returnStatus = bind(*sockSched, (SA*)addrSched,sizeof(*addrSched));
	if (returnStatus < 0)
	{
		perror("MainThread : bind");
		close(*sockSched);
		return EXIT_FAILURE;
	}
	
	return EXIT_SUCCESS;
}


/* =============================================================== *
 *		         RECEIVE MESSAGE			   *
 * =============================================================== */


int receive_message( int sockSched, struct sockaddr_in * addrPeer, char * message)
{
	
	int returnStatus = 0;
	socklen_t addrlen;
	char bufctrl[MAXMES];

	sprintf(log_msg, "MainThread::Receive_Message : Wait for connections .\n");
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* receive demand(DEM) from Client or register(REG) form Server */
	addrlen = sizeof(*addrPeer);
	returnStatus = recvfrom(sockSched, bufctrl, MAXMES, 0,(SA*)addrPeer, &addrlen);
	if (returnStatus < 0) 
	{
		perror("MainThread:: Receive_Message : recvfrom\n");
		return EXIT_FAILURE;
	}  
	
	// Plus
	bufctrl[returnStatus] = '\0';
	
	/* accept only DEM or REG messages*/
	if (  (strcmp( bufctrl, dem) != 0) && ( strcmp( bufctrl, reg) != 0) ) 
	{
		// Debug 
		sprintf( log_msg, "MainThread:: Receive_Message : Ignored : %s from %s@%d \n", bufctrl, inet_ntoa(addrPeer->sin_addr), ntohs(addrPeer->sin_port));
		print_msg( log_msg, 1, 0, 1, log_file);
		return EXIT_FAILURE;
	}
	
	/* copy the received message */
	strcpy( message, bufctrl);
	
	//sprintf(log_msg, "MainThread:: Receive_Message : Received %s .\n", bufctrl);
	//print_msg( log_msg, 1, 0, 1, log_file);
	
	return EXIT_SUCCESS;

}


/* =============================================================== *
 *		              SAVE PEER				   *
 * =============================================================== */

int save_peer( int sockSched, struct sockaddr_in addrPeer, const char * message, LList * serv_queue, Queue * serv_idle_queue, Queue * clnt_queue) 
{
	
	int returnStatus = 0;		// for each fuction call
	
	int addrlen = 0;		// size of sockaddr_in
	struct clientaddr * newClient;
	struct serveraddr * newServer;


	/* save server address if it is a REG message */
	if ( strcmp( message, reg) == 0 )
	{
		// Debug 
		//sprintf( log_msg, "main thread : Received: %s from %s@%d \n", bufctrl, inet_ntoa(addrPeer.sin_addr), ntohs(addrPeer.sin_port));
		//print_msg( log_msg, 1, 0, 1, log_file);
		
		/* init the server address */
		newServer = malloc(sizeof(struct serveraddr));
		memset( newServer, 0, sizeof(struct serveraddr));
		memcpy( &(newServer->addr), &addrPeer, sizeof(struct sockaddr_in));
		strcpy( (newServer->reg_message), message);//en plus
		newServer->status = IDLE;
			
		pthread_mutex_lock( &(serv_queue->mutex) );
		sprintf( log_msg, "MainThread:: Save_Peer : get servers queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		/* enqueue the server addr */
		llist_insert_last(serv_queue,(void*) newServer);
		pthread_mutex_unlock( &(serv_queue->mutex) );
		sprintf( log_msg, "MainThread:: Save_Peer : less servers queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
			
		pthread_mutex_lock( &(serv_idle_queue->mutex) );
		sprintf( log_msg, "MainThread:: Save_Peer : get servers idle queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		queue_enqueue(serv_idle_queue,(void*) newServer);
		pthread_mutex_unlock( &(serv_idle_queue->mutex) );
		sprintf( log_msg, "MainThread:: Save_Peer : less servers queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
			
		// debug
		sprintf(log_msg, "MainThread:: Save_Peer : server address %s@%d registred at %p .\n", inet_ntoa(newServer->addr.sin_addr), ntohs(newServer->addr.sin_port), newServer);//*/
		print_msg( log_msg, 1, 0, 1, log_file);
			
		/* send ACK */
		returnStatus = sendto(sockSched, message, strlen(message), 0,(SA*)&addrPeer,sizeof(addrPeer));
		if (returnStatus < 0 ) 
		{
			perror("MainThread : sendto .\n");
			sprintf(log_msg, "MainThread:: Save_Peer : Could not send confirmation to server %s@%d .\n",  inet_ntoa(addrPeer.sin_addr), ntohs(addrPeer.sin_port));
			print_msg( log_msg, 0, 1, 1, log_file);
		}
		
		// send a signal if the filed elt is the first
		pthread_mutex_lock( &(serv_idle_queue->mutex_non_empty) );
		pthread_cond_signal( &(serv_idle_queue->cond_non_empty) );
		sprintf( log_msg, "MainThread:: Save_Peer : send signal non-empty servers.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		pthread_mutex_unlock( &(serv_idle_queue->mutex_non_empty) );
				
		
		return EXIT_SUCCESS ;
	}
		
	/* save client address if it is a DEM message */
	if ( strcmp( message, dem) == 0 )
	{
		// Debug 
		//sprintf( log_msg, "main thread : Received: %s from %s@%d \n", bufctrl, inet_ntoa(addrPeer.sin_addr), ntohs(addrPeer.sin_port));
		//print_msg( log_msg, 1, 0, 1, log_file);	
			
		/* enqueue the client address */
		newClient = malloc(sizeof(struct clientaddr));
		memset( newClient, 0, sizeof(struct clientaddr)); 
		newClient->addr.sin_family = addrPeer.sin_family;
		newClient->addr.sin_port = addrPeer.sin_port;
		newClient->addr.sin_addr = addrPeer.sin_addr;
		strcpy(newClient->dem_message, message);// obligatooire
			
		pthread_mutex_lock( &(clnt_queue->mutex) );
		sprintf( log_msg, "MainThread:: Save_Peer : get clients queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		queue_enqueue(clnt_queue,(void*) newClient);
		pthread_mutex_unlock( &(clnt_queue->mutex) );
		sprintf( log_msg, "MainThread:: Save_Peer : less clients queue mutex.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
			
		// debug
		sprintf(log_msg, "MainThread:: Save_Peer : client address %s@%d registred at %p .\n", inet_ntoa(newClient->addr.sin_addr), ntohs(newClient->addr.sin_port), newClient);//*/
		print_msg( log_msg, 1, 0, 1, log_file);
			
		// send a signal if the filed elt is the first
		pthread_mutex_lock( &(clnt_queue->mutex_non_empty) );
		pthread_cond_signal( &(clnt_queue->cond_non_empty) );
		sprintf( log_msg, "MainThread:: Save_Peer : send signal non-empty clients.\n");
		print_msg( log_msg, 1, 0, 1, log_file);
		
		pthread_mutex_unlock( &(clnt_queue->mutex_non_empty) );
			
		return EXIT_SUCCESS ;	
	}
}

/* =============================================================== *
 *		        CREATE UDP SOCKET			   *
 * =============================================================== */

int create_udp_socket(int * sockfd)
{
	*sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (*sockfd < 0)
	{
		perror("socket");
		return EXIT_FAILURE;
	}
	
	return EXIT_SUCCESS;
}

/* =============================================================== *
 *		        START COMMUNICATION			   *
 * =============================================================== */

int start_communication( int sockNew, struct clientaddr client, struct sockaddr_in addrServ)
{
	
	char bufctrl[MAXMES];
	int returnStatus;
	struct sockaddr_in addrNew;
	socklen_t addrlen;
	
	
	char buffile[MAXBUFFILE];
	
	/* send job message to the server */
	strcpy(bufctrl, job);
	returnStatus = sendto(sockNew, bufctrl, strlen(bufctrl), 0,(SA*)&addrServ,sizeof(addrServ));
	if (returnStatus < 0 ) 
	{
		perror("sendto");
		sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not send job message (%s) to server.\n", (long) pthread_self(), bufctrl);
		print_msg( log_msg, 0, 1, 1, log_file);
		return EXIT_FAILURE;
	}
	
	// Debug
	sprintf(log_msg, "ClntThread::Start_Communication %ld : Send job message (%s) to server.\n", (long) pthread_self(), bufctrl);
	print_msg( log_msg, 1, 0, 1, log_file);
	
	// debug
	getsockname(sockNew, (SA *)&addrNew, &addrlen);
			
	// Debug
	sprintf(log_msg,"ClntThread::Start_Communication %ld : Socket  %d binded %s@%d \n",(long) pthread_self(), sockNew, inet_ntoa(addrNew.sin_addr), ntohs(addrNew.sin_port));
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* send ACK to the client */
	strcpy(bufctrl, client.dem_message);
	returnStatus = sendto(sockNew, bufctrl, strlen(bufctrl), 0,(SA*)&(client.addr),sizeof(client.addr));
	if (returnStatus < 0 ) 
	{
		perror("sendto");
		sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not send confirmation (%s) to client.\n", (long) pthread_self(), bufctrl);
		print_msg( log_msg, 0, 1, 1, log_file);
		return EXIT_FAILURE;
	}
	
	// Debug
	sprintf(log_msg, "ClntThread::Start_Communication %ld : Send confirmation (%s) to client.\n", (long) pthread_self(), bufctrl);
	print_msg( log_msg, 1, 0, 1, log_file);
	sprintf(log_msg, "ClntThread::Start_Communication %ld : Receiving program from client and send it to the server.\n", (long) pthread_self());
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* receive program from client and send it to the server */
	while(1)
	{
		/* receive message */
		addrlen = sizeof(struct sockaddr_in);
		returnStatus = recvfrom( sockNew, buffile, MAXBUFFILE, 0,(SA*)&(client.addr), &addrlen);
		if (returnStatus < 0) 
		{
			perror("recvfrom");
			sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not receive message.\n", (long) pthread_self());
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
			
		// plus
		buffile[returnStatus] = '\0';
		
		// debug
		//sprintf( log_msg, "client thread %ld : Received: %s from %s@%d \n", (long) pthread_self(), buffile, inet_ntoa(addrClnt.sin_addr), ntohs(addrClnt.sin_port));
		//print_msg( log_msg, 1, 0, 1, log_file);
		
		/* send message to the server */
		returnStatus = sendto(sockNew, buffile, returnStatus, 0,(SA*)&addrServ, sizeof(struct sockaddr_in));
		if (returnStatus < 0) 
		{
			perror("sendto");
			sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not send message to the server.\n", (long) pthread_self());
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		
		//debug
		//sprintf(log_msg, "client thread %ld : Send %s to the server %s@%d .\n",  (long) pthread_self(), buffile,inet_ntoa(addrServ.sin_addr), ntohs(addrServ.sin_port));
		//print_msg( log_msg, 1, 0, 1, log_file);
		
		if (strcmp(buffile, progterm) == 0)
		   break;
	}


	sprintf(log_msg, "ClntThread::Start_Communication %ld : Receiving arguments from client and send them to the server.\n", (long) pthread_self());
	print_msg( log_msg, 1, 0, 1, log_file);
	
	/* Receiving arguments from client and send them to server  */
	while(1)
	{
		/* receive arguments */
		addrlen = sizeof(struct sockaddr_in);
		returnStatus = recvfrom(sockNew, buffile, MAXBUFFILE, 0,(SA*)&(client.addr), &addrlen);
		if (returnStatus < 0) 
		{
			perror("recvfrom");
			sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not receive arguments.\n", (long) pthread_self());
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		
		/* send arguments to server */
		returnStatus = sendto(sockNew, buffile, returnStatus, 0,(SA*)&addrServ,sizeof(struct sockaddr_in));
		if (returnStatus < 0) 
		{	
			perror("sendto");
			sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not send arguments\n", (long) pthread_self());
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		
		/* Check if it is a TERMARG */
		// plus
		buffile[returnStatus] = '\0';
		if(strcmp(buffile,argterm)==0) break;				
	}
	
	/* Receive results from server, Send results to client */
	while(1)
	{
		/* receive results */
		addrlen = sizeof(struct sockaddr_in);
		returnStatus = recvfrom( sockNew, buffile, MAXBUFFILE, 0,(SA*)&addrServ, &addrlen);
		if (returnStatus < 0) 
		{
			perror("recvfrom");
			sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not receive results.\n", (long) pthread_self());
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		// plus
		buffile[returnStatus] = '\0';
		
		// debug
		//sprintf( log_msg, "clnt thread %ld : Received Results: \n\n%s \n\nfrom %s@%d \n", (long) pthread_self(), buffile, inet_ntoa(addrServ.sin_addr), ntohs(addrServ.sin_port));
		//print_msg( log_msg, 1, 0, 1, log_file);
		
		/* send back results to the client */
		returnStatus = sendto(sockNew, buffile, returnStatus, 0,(SA*)&(client.addr),sizeof(struct sockaddr_in));
		if (returnStatus < 0) 
		{
			perror("sendto");
			sprintf(log_msg, "ClntThread::Start_Communication %ld : Could not send results to the client.\n", (long) pthread_self());
			print_msg( log_msg, 0, 1, 1, log_file);
			return EXIT_FAILURE;
		}
		
		if (strcmp(buffile, resultsterm) == 0)
		   break;
		
		//debug
		sprintf(log_msg, "ClntThread::Start_Communication %ld : Sending Results  \n%s \nto the client %s@%d .\n", (long) pthread_self(), buffile,inet_ntoa(client.addr.sin_addr), ntohs(client.addr.sin_port));
		print_msg( log_msg, 1, 0, 1, log_file);
		
	}
	
	close(sockNew);
	return EXIT_SUCCESS;	
	
}



